<?php

declare(strict_types=1);

namespace app;

/**
 * 微信相关的公共服务
 */
class WxCommon
{
    /**
     * 验证签名
     *
     * @return boolean
     */
    public static function verifySign(string $strSign, string $strTimeStamp, string $strNonce, string $strEncrypt): bool
    {
        if (
            empty($strSign)
            || empty($strTimeStamp)
            || empty($strNonce)
            || empty($strEncrypt)
        ) {
            return false;
        }
        //生成签名
        $strNewSign = sha1($strTimeStamp . $strNonce . env('WECHAT_TOKEN') . $strEncrypt);

        //校验签名
        if ($strSign != $strNewSign) {
            return false;
        }
        return true;
    }

    //解密消息体
    public static function decryptMsg(string $strEncrypt): array
    {
        if (empty($strEncrypt)) {
            return [];
        }
        //解密消息体
        $strAESKey = base64_decode(env('WECHAT_ENCODING_AES_KEY') . '='); //获取AES密钥
        $strTmpMsg = base64_decode($strEncrypt);

        $strDecrypted = openssl_decrypt(
            $strTmpMsg,
            'AES-256-CBC',
            $strAESKey,
            OPENSSL_RAW_DATA | OPENSSL_NO_PADDING,
            substr($strAESKey, 0, 16)
        );

        // 去掉 PKCS#7 填充
        $strFullStr = self::unPadPKCS7($strDecrypted, 32);

        // 去掉头部 16 字节随机串 + 4 字节 msg_len + 尾部的 AppId
        // 格式：rand(16) + msg_len(4) + msg + appid
        if (strlen($strFullStr) < 20) {
            throw new \Exception('Plain too short');
        }
        $msgLen = unpack('N', substr($strFullStr, 16, 4))[1];
        $strJsonMsg    = substr($strFullStr, 20, $msgLen);
        if ($strJsonMsg === false) {
            throw new \Exception('Unpack msg failed');
        }
        $strAppId = substr($strFullStr, 20 + $msgLen);
        if ($strAppId !== env('WECHAT_APPID')) {
            throw new \Exception('Invalid AppId');
        }

        $arrRes = json_decode($strJsonMsg, true);
        if (empty($arrRes)) {
            throw new \Exception('Invalid json');
        }
        $arrRes['appid'] = $strAppId;

        return $arrRes;
    }

    /**
     * 去除 PKCS#7 填充
     */
    private static function unPadPKCS7(string $text, int $blockSize): string
    {
        $pad = ord($text[strlen($text) - 1]);
        if ($pad < 1 || $pad > $blockSize) {
            throw new \Exception('Invalid PKCS#7 pad');
        }
        if (substr($text, -$pad) !== str_repeat(chr($pad), $pad)) {
            throw new \Exception('Invalid PKCS#7 pad');
        }
        return substr($text, 0, -$pad);
    }
}
